/* test_threads_func_uthread_join_2():
	A test for uthread_join() - test a special case.

	* We create the additional threads: 1,2,3.
	* Thread 1 joins thread 2.
	* Thread 2 joins thread 3 (thread 3 is doing infinit operation).
	* Thread 0 cancels thread 2.
	* Waiting a bit.
	* Thread 0 cancels thread 3.
*/


#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include "test_general.h"
#include "user_threads.h"



bool test_threads_func_uthread_join_2();

#define THREADS_NUM 4

static int thread1_start_routine(void * param) {
	TEST_ASSERT (uthread_self() == 1);

	some_long_operation(0.1);
	int tmp = uthread_join(2,NULL);
	TEST_ASSERT(UTHREAD_SUCCESS ==  tmp);

	some_long_operation(0.01);
	thread_ok[1] = true;
	return 0;
}


static int thread2_start_routine(void * param) {
	TEST_ASSERT (uthread_self() == 2);

	some_long_operation(0.1);
	thread_ok[2] = true;
	int tmp = uthread_join(3,NULL);
	TEST_ASSERT(UTHREAD_SUCCESS ==  tmp);

	return 0;
}

static int thread3_start_routine(void * param) {
	TEST_ASSERT (uthread_self() == 3);

	thread_ok[3] = true;
	some_long_operation(100.0); // Until being canceled.
	TEST_ASSERT(false);
	return 0;
}


bool test_threads_func_uthread_join_2()
{
	init_individual_test();

	int tmp;

	if (initilize_uthread_in_each_test) {
		tmp = uthread_init();
		TEST_ASSERT(tmp == UTHREAD_SUCCESS);
	}


	thread_id threads[THREADS_NUM];

	threads[0] = uthread_self();


	int FiftyThree = 53;

	threads[1] = uthread_create(thread1_start_routine, &FiftyThree);

	threads[2] = uthread_create(thread2_start_routine, &FiftyThree);

	threads[3] = uthread_create(thread3_start_routine, &FiftyThree);

	TEST_ASSERT(threads[1] == 1);
	TEST_ASSERT(threads[2] == 2);
	TEST_ASSERT(threads[3] == 3);

	some_long_operation(0.3);
	tmp = uthread_cancel(threads[2]);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);

	some_long_operation(0.3);

	TEST_ASSERT(last_running_times[1] < last_running_times[0]-0.5);

	tmp = uthread_cancel(threads[3]);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);

	some_long_operation(0.1);

	TEST_ASSERT(last_running_times[1] > last_running_times[0]-0.1);

	tmp = uthread_join(threads[1],NULL);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);

	TEST_ASSERT(get_precise_time()-last_running_times[0] < 0.05);

	thread_ok[0] = true;
	TEST_ASSERT(are_all_threads_ok(THREADS_NUM));
	return true;
}
